package com.hj.tools;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件操作类
 * 可在生产环境（Web容器）下和开发环境下保持一致的使用方法
 * 对于相对路径，可通过setBasePath方法指定前缀
 * 开发环境下的相对路径其前缀为用户当前目录：System.getProperty("user.dir")
 * 生产环境（Web容器）下的相对路径其前缀为应用的根目录：servletContextEvent.getServletContext().getRealPath("/")
 * @author HJ
 */
public class FileUtil {
	private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);
        
    /**
     * 工具类，无实例对象，仅仅提供静态方法
     */
    private FileUtil(){};
    
    private static String basePath = System.getProperty("user.dir").replace("\\", "/");
    
    /**
     * 系统的默认目录为当前用户目录，可通过此函数重新设置
     * @param basePath 相对路径前缀
     */
    public static void setBasePath(String basePath) {
    	LOG.info("设置文件资源路径: {}", basePath);
        FileUtil.basePath = basePath.replace("\\", "/");
    }
    
    /**
     * 追加写入文件 -- 另起一行
     * @param path 文件绝对路径
     * @param text 要追加的文件内容
     * @return 是否追加成功
     */
    public static boolean appendTextInNewLine(String path,String text){
        return appendText(path,text+System.getProperty("line.separator"));
    }
    
    /**
     * 追加写入文件
     * @param path 文件绝对路径
     * @param text 要追加的文件内容
     * @return 是否追加成功
     */
    public static boolean appendText(String path,String text){
        try{
            File file=new File(path);
            if (!file.exists()) {
            	file.getParentFile().mkdirs();
            	file.createNewFile();
			}
            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true), "utf-8"))) {
                writer.write(text);
                writer.close();
            }
            return true;
        }catch(IOException e){
            LOG.error("写文件出错",e);
        }
        return false;
    }
   
    /**
     * 获取 web根目录或当前工作目录或指定目录 下面的资源的绝对路径
     * / 返回 web根目录或当前工作目录或指定目录 的绝对路径
     * /WEB-INF 返回 web根目录或当前工作目录或指定目录 下的WEB-INF 的绝对路径
     * WEB-INF 返回 web根目录或当前工作目录或指定目录 下的WEB-INF 的绝对路径
     * 
     * 特殊：对windows下的绝对路径不做转换    
     * c:/test/test.txt 返回 c:/test/test.txt
     * 
     * @param path 相对于WEB根目录的路径
     * @return 绝对路径
     */
    public static String getAbsolutePath(String path){
        //在windows下，如果路径包含：,为绝对路径，则不进行转换
        if(path.contains(":")){
            return path.replace("\\", "/");
        }
        
        LOG.debug("转换路径:"+path);        
        path = path.replace("\\", "/").trim();        
        path = basePath+"/" + path;
        while(path.contains("//")){
            path=path.replace("//", "/");
        }
        LOG.debug("返回路径:"+path);
        return path;
    }
    /**
     * 文件复制
     * @param inFile 输入文件
     * @param outFile 输出文件
     */
    public static void copyFile(File inFile, File outFile){
        try {
            copyFile(new FileInputStream(inFile),outFile);
        } catch (FileNotFoundException ex) {
            LOG.error("文件不存在",ex);
        }
    }
    /**
     * 把输入流中的内容拷贝到一个文件
     * @param in 输入流
     * @param outFile 文件对象
     */
    public static void copyFile(InputStream in, File outFile){
        OutputStream out = null;
        try {
            byte[] data=readAll(in);
            out = new FileOutputStream(outFile);
            out.write(data, 0, data.length);
            out.close();
        } catch (Exception ex) {
            LOG.error("文件操作失败",ex);
        } finally {
            try {
                if(in!=null){
                    in.close();
                }
            } catch (IOException ex) {
             LOG.error("文件操作失败",ex);
            }
            try {
                if(out!=null){
                    out.close();
                }
            } catch (IOException ex) {
             LOG.error("文件操作失败",ex);
            }
        }
    }
    /**
     * 读取一个文件的所有字节
     * @param file 文件对象
     * @return 字节数组
     */
    public static byte[] readAll(File file){
        try {
            return readAll(new FileInputStream(file));
        } catch (Exception ex) {
            LOG.error("读取文件失败",ex);
        }
        return null;
    }
    /**
     * 从输入流中读取所有字节
     * @param in 输入流
     * @return 字节数组
     */
    public static byte[] readAll(InputStream in){
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            for (int n ; (n = in.read(buffer))>0 ; ) {
                out.write(buffer, 0, n);
            }
        } catch (IOException ex) {
            LOG.error("读取文件失败",ex);
        }finally{
        	try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
        }
        byte[] data = out.toByteArray();
        return data;
    }
    
    /**
     * 从类路径下读取文件中所有字节
     * @param filename 类路径下的文件
     * @return 字节数组
     */
    public static byte[] readAll(String filename){
    	byte[] data = null;
		InputStream in = null;
		try {
			in = FileUtil.class.getResourceAsStream(filename);
			data = readAll(in);
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return data;
    }
    
    /**
     * 获取一个文件的输入流
     * @param path 相对路径或绝对路径
     * @return 文件输入流
     */
    public static FileInputStream getInputStream(String path) {
        try {
            return new FileInputStream(getAbsolutePath(path));
        } catch (FileNotFoundException ex) {
            LOG.error("文件没有找到",ex);
        }
        return null;
    }
    /**
     * 判断一个文件是否存在
     * @param path 相对路径或绝对路径
     * @return 是否存在
     */
    public static boolean existsFile(String path){
        try{
            File file=new File(getAbsolutePath(path));
            if(file.exists()){
                return true;
            }
        }catch(Exception ex){
            LOG.error("文件操作失败",ex);
        }
        return false;
    }
    /**
     * 把字节内容写入新文件
     * @param path 相对路径或绝对路径
     * @param data 字节数组
     * @return 新文件
     */
    public static File createAndWriteFile(String path, byte[] data){
        try{
            File file=new File(getAbsolutePath(path));
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            file.createNewFile();
            try (OutputStream out = new FileOutputStream(file)) {
                out.write(data, 0, data.length);
                out.close();
            }
            return file;
        }catch(Exception ex){
            LOG.error("文件操作失败",ex);
        }
        return null;
    }
    /**
     * 把文本内容写入一个新文件
     * 如果文件已经存在
     * 则覆盖
     * @param path 相对路径或绝对路径
     * @param text 文本
     * @return 文件
     */
    public static File createAndWriteFile(String path, String text){
        try{
            File file=new File(getAbsolutePath(path));
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            file.createNewFile();
            try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"utf-8"))) {
                writer.write(text);
            }
            return file;
        }catch(Exception ex){
            LOG.error("文件操作失败",ex);
        }
        return null;
    }
    /**
     * 删除文件
     * @param path 相对路径或绝对路径
     * @return 是否成功
     */
    public static boolean removeFile(String path){
        try{
            File file=new File(getAbsolutePath(path));
            if(file.exists()){
                if(!file.delete()){
                    file.deleteOnExit();
                }
            }
            return true;
        }catch(Exception ex){
            LOG.error("文件操作失败",ex);
        }
        return false;
    }
    /**
     * 获取文本内容
     * @param path 相对路径或绝对路径
     * @return 行的列表
     */
    public static List<String> getTextFileContent(String path) {
        try {
            return getTextFileContent(new FileInputStream(getAbsolutePath(path)));
        } catch (FileNotFoundException ex) {
            LOG.error("文件不存在", ex);
        }
        //Null Object设计模式
        return Collections.emptyList();
    }
    /**
     * 获取输入流中的文本内容
     * @param in 文本文件输入流
     * @return 行的列表
     */
    public static List<String> getTextFileContent(InputStream in) {
        List<String> result = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
            for (;;) {
                String line = reader.readLine();
                if (line == null)
                    break;
                result.add(line);
            }
        } catch(Exception e){
            LOG.error("读取文件失败",e);
        }
        return result;
    }
    
    /**
     * 使用递归获取文件目录，删除不需要的文件后缀文件列表
     * @param filedir
     * @param suffixList
     */
    public void startDeleteFixedFiles(String filedir, List<String> suffixList) {
		if (null == filedir || "".equals(filedir.trim())) {
			System.out.println("filedir 目录不对！");
			return;
		}
		filedir = filedir.trim();
		if (null == suffixList || suffixList.size() <= 0) {
			System.out.println("suffixList 没有要匹配的后缀！");
			return;
		}
		File f = new File(filedir);
		if (f.isDirectory()) {
			handleFile(f, suffixList);
		} else {
			System.out.println("filedir 必须为目录");
			for (String suffix : suffixList) {
				if (f.getName().endsWith(suffix)) {
					// 匹配到的要删除
					try {
						f.delete();
					} catch (Exception e) {
						System.out.println("文件删除失败：" + f.getAbsolutePath()
								+ "\\" + f.getName());
					}
				}
			}
		}
	}

	private void handleFile(File filedir, List<String> suffixList) {
		// 目录
		File[] files = filedir.listFiles();
		for (File subFile : files) {
			if (subFile.isDirectory()) {
				handleFile(subFile, suffixList);
			} else {
				// 文件
				for (String suffix : suffixList) {
					if (subFile.getName().endsWith(suffix)) {
						// 匹配到的要删除
						try {
							subFile.delete();
							System.out.println("已删除文件："
									+ subFile.getAbsolutePath() + "\\"
									+ subFile.getName());
						} catch (Exception e) {
							System.out.println("文件删除失败："
									+ subFile.getAbsolutePath() + "\\"
									+ subFile.getName());
						}
					}
				}
			}
		}
	}
	
	/**
	 * 批量修改指定文件夹下所有后缀名的文件为另外后缀名的代码
	 */
	public static void batchRenameFile(String dir, String srcSuffix, String dstSuffix){
		File file = new File(dir);
		List<String> paths = listPath(file, srcSuffix);
		for (String path : paths) {
			File srcFile = new File(path);
			String name = srcFile.getName();
			int idx = name.lastIndexOf(".");
			String prefix = name.substring(0, idx);
			File dstFile = new File(
					srcFile.getParent() + "/" + prefix + "."+ dstSuffix);
			if (dstFile.exists()) {// 此处代码注意修改为自己想要的逻辑
				srcFile.delete();
				continue;
			}
			srcFile.renameTo(dstFile);
		}
	}

	/**
	 * 获取指定路径下的所有符合条件的路径
	 */
	private static List<String> listPath(File path, String acceptSuffix) {
		List<String> list = new ArrayList<String>();
		File[] files = path.listFiles();
		Arrays.sort(files);
		for (File file : files) {
			if (file.isDirectory()) {
				List<String> _list = listPath(file, acceptSuffix);
				list.addAll(_list);
			} else {
				String name = file.getName();
				int idx = name.lastIndexOf(".");
				String suffix = name.substring(idx + 1);
				if (suffix.equals(acceptSuffix)) {
					list.add(file.getAbsolutePath());
				}
			}
		}
		return list;
	}
	
	/**
     * 读取文件最后N行 
     * 
     * 根据换行符判断当前的行数，
     * 使用统计来判断当前读取第N行
     * 
     * PS:输出的List是倒叙，需要对List反转输出
     * 
     * @param file 待文件
     * @param numRead 读取的行数
     * @return List<String>
     */
    public static List<String> readLastNLine(File file, long numRead){
		// 定义结果集
		List<String> result = new ArrayList<String>();
		// 行数统计
		long count = 0;

		// 排除不可读状态
		if (!file.exists() || file.isDirectory() || !file.canRead()) {
			return null;
		}

		// 使用随机读取
		RandomAccessFile fileRead = null;
		try {
			// 使用读模式
			fileRead = new RandomAccessFile(file, "r");
			// 读取文件长度
			long length = fileRead.length();
			// 如果是0，代表是空文件，直接返回空结果
			if (length == 0L) {
				return result;
			} else {
				// 初始化游标
				long pos = length - 1;
				while (pos > 0) {
					pos--;
					// 开始读取
					fileRead.seek(pos);
					// 如果读取到\n代表是读取到一行
					if (fileRead.readByte() == '\n') {
						// 使用readLine获取当前行
						String line = fileRead.readLine();
						// 保存结果
						result.add(line);

						// 打印当前行
						System.out.println(line);

						// 行数统计，如果到达了numRead指定的行数，就跳出循环
						count++;
						if (count == numRead) {
							break;
						}
					}
				}
				if (pos == 0) {
					fileRead.seek(0);
					result.add(fileRead.readLine());
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fileRead != null) {
				try {
					// 关闭资源
					fileRead.close();
				} catch (Exception e) {
				}
			}
		}
        
        return result;
    }
    
    /*public static void main(String[] args) throws UnsupportedEncodingException {
    	String path = ClassLoader.class.getResource("/").getPath();
		try {
			URLDecoder.decode(path, "UTF-8");
		} catch (UnsupportedEncodingException e) {
		}
    	System.out.println(path);
    	Collection<String>  list = getTextFileContent("config/jpa.xml");
    	for (String s : list) {
			System.out.println(s);
		}
	}*/
}